import wx
import datetime
import calendar
from SimpleCalendarItem import SimpleCalendarItem, EVT_SELECTED
from SimpleWeekTitle import SimpleWeekTitle

MODE_MONTH = 1
MODE_WEEK = 2
MODE_DAY = 3
DEFAULT_DAY_WIDTH = 500
DEFAULT_WEEK_WIDTH = 190
DEFAULT_LENGTH_L = 800
DEFAULT_LENGTH_S = 150
NO_TITLE = 0
WITH_TITLE = 1

class SimpleCalendarCtrl(wx.ScrolledWindow):
    """
    container class
    """
    def __init__(self, parent, mode=MODE_DAY,
                 reference=datetime.datetime.now().date(),
                 style=NO_TITLE):
        """
        what to do?
        """
        wx.ScrolledWindow.__init__(self, parent, -1)
        self.SetScrollbars(1, 1, 1400, 1800)
        self.vsizer = wx.GridBagSizer()
        self.SetSizer(self.vsizer)
        #container for SimpleCalendarItems
        self.subviews = {}
        #container for all the todo list item added to ctrl
        self.daylists = {}
        self.dirtyviews = {}
        self.reference = reference
        self.mode = None
        #we need some space!
        self.SetScrollRate(10,40)
        #this is for title
        self.style = style
        self.init_title()

        self.change_mode_ref(mode, reference)

    def init_title(self):
        self.titles = []
        for j,i in enumerate(('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')):
            #I am really sick of aligning things in wxpython
            w = WeekLabelWindow(self, i)
            self.titles.append(w)
            self.vsizer.Add(w, (0,j), flag=wx.EXPAND)

    def add(self, day, start, end, event_string, color='#8cf0ec', sync = 0):
        """
        add a event item to a day
        day: a datetime object will do. For we can do comparation on it
        NOTE1: calendar item keeps a reference of daylists item that calendarctrl
        also keeps, so change one and the other will see it change
        NOTE2: calendar control MUST NOT forget those item that user gave it. So
        calendarctrl must keep a data copy of those day, but only 1 copy
        NOTE3: when you use MODE_MONTH, I am sure that there will be a lot of empty
        days, TODO: can we find a better way of doing this?
        """
        if not self.daylists.has_key(day):
            #this should could happen when calendar is not on the show
            #it could also happen on empty calendar days
            l = self.daylists[day] = []
            need_associate = 1
        else:
            l = self.daylists[day]
            need_associate = 0
        #still NOTE: ctrl and item share a list
        l.append((start, end, event_string, color, None))
        #the following code deal with GUI part
        if self.is_on_display(day):
            #check if we need to associate
            if need_associate:
                self.subviews[day].list = l
            #check if we need to redraw right now
            if sync == 0:
                #just add
                self.dirtyviews[day] = 1
            else:
                if self.is_on_display(day):
                    #TODO:update this single day
                    self.subviews[day].update()
        

    def remove(self, day, start, sync = 0):
        """
        delete a event item from calendar
        """
        if not self.daylists.has_key(day):
            print("attempt to delete something that do not exist in calendar")
            return
        l = self.daylists[day]
        for i in l:
            if i[0] == start:
                break
        l.remove(i)
        if len(l) == 0:
            del self.daylists[day]
            #I do not think we need to delete the list on subview side, for when
            #subview is deleted, that list will be gone too
        if self.is_on_display(day):
            if sync == 0:
                self.dirtyviews[day] = 1
            else:
                #only update this single day
                self.subviews[day].update()

    def remove_all(self):
        """
        remove every item from calendar
        """
        #TODO: just remove all todo items is not enough i think
        self.daylists = {}

    def change_mode_ref(self, mode, ref):
        """
        change display mode to mode
        and i decided that we do not delete any thing here
        """
        if mode == self.mode and self.is_on_display(ref):
            #lucky
            return
        
        self.mode = mode
        self.reference = ref
        #find out candidates
        if mode == MODE_DAY:
            candidates = [self.reference]
            width = DEFAULT_DAY_WIDTH
            length = DEFAULT_LENGTH_L
            cols = 1
            rows = 1
        elif mode == MODE_MONTH:
            itr = calendar.Calendar(6).itermonthdates(ref.year, ref.month)
            candidates = [i for i in itr]
            width = DEFAULT_WEEK_WIDTH
            length = DEFAULT_LENGTH_S
            #print(len(candidates))
            cols = 7
            rows = 7#or4
        elif mode == MODE_WEEK:
            p = datetime.datetime.weekday(ref)
            delta = datetime.timedelta(1)
            candidates = [(ref-(p-i)*delta) for i in range(0,7)]
            width = DEFAULT_WEEK_WIDTH
            length = DEFAULT_LENGTH_L
            cols = 7
            rows = 1
        else:
            print("what the fuck is this mode?%d"%(mode))
        to_add = candidates
        #before adding new, get rid of(clearly) old ones
        #delete all views, for they are definately not reusable, different sizes
        for i in self.subviews:
            it = self.subviews[i]
            self.vsizer.Remove(it)
            it.Destroy()
        self.subviews = {}
        #MODE_MONTH need small view, which do not show text
        if mode == MODE_MONTH:
            pm = "SMALL"
        else:
            pm = "BIG"
        for i in to_add:
            #create corrospending lists
            if self.daylists.has_key(i):
                l = self.daylists[i]
            else:
                l = self.daylists[i] = []
            newv = SimpleCalendarItem(self, i, l, width=width, length=length,
                                      mode=pm, event_mode=0)
            self.subviews[i] = newv
            
            #self.subviews[i] = SimpleCalendarItem(self, i)
        if mode == MODE_DAY:
            self.fit_in_day(candidates)
        if mode == MODE_WEEK:
            self.fit_in_week(candidates)
        if mode == MODE_MONTH:
            self.fit_in_month(candidates)
            
        self.Layout()
        #self.update()
            
    def fit_in_day(self, candidates):
        for i in self.titles:
            i.Hide()
        self.vsizer.Add(self.subviews[candidates[0]], (1,0))

    def fit_in_week(self, candidates):
        for i in self.titles:
            i.Show()
        for j,i in enumerate(candidates):
            self.vsizer.Add(self.subviews[i], (1,j))

    def fit_in_month(self, candidates):
        for i in self.titles:
            i.Show()
        col = 0
        row = 1
        for i in candidates:
            self.vsizer.Add(self.subviews[i], (row, col))
            col += 1
            if col == 7:
                col = 0
                row += 1

    def update(self):
        """
        sync data and UI
        """
        for i in self.dirtyviews:
            self.subviews[i].update()

    def is_on_display(self, day):
        return self.subviews.has_key(day)

    def deal_on_select(self, data):
        evt = wx.PyCommandEvent(EVT_SELECTED.typeId, self.GetId())
        evt.mydata = data
        evt.SetEventObject(self)
        self.GetEventHandler().ProcessEvent(evt)
        #print(self)

class WeekLabelWindow(wx.Window):
    def __init__(self, parent, text):
        wx.Window.__init__(self, parent, style=wx.RAISED_BORDER)
        sz = wx.BoxSizer(wx.HORIZONTAL)
        title = wx.StaticText(self, label=text, style=wx.ALIGN_CENTER)
        #sz.Add(title, 1, wx.ALL|wx.EXPAND)
        #self.SetSizer(sz)

class TestFrame(wx.Frame):
    def __init__(self, parent=None):
        wx.Frame.__init__(self, parent,
                          size = (500,500),
                          title="just a test",
                          style=wx.DEFAULT_FRAME_STYLE)
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.mode = MODE_DAY
        self.day = datetime.datetime.now()
        
        uut = self.uut = SimpleCalendarCtrl(self, mode=self.mode)
        calen = cal.CalendarCtrl(self, -1, wx.DateTime_Now(),
            style = cal.CAL_SHOW_HOLIDAYS|cal.CAL_SHOW_SURROUNDING_WEEKS|cal.CAL_MONDAY_FIRST)
        btn = wx.Button(self, -1, "change")

        self.Bind(wx.EVT_BUTTON, self.on_change_button, btn)
        self.Bind(cal.EVT_CALENDAR_SEL_CHANGED, self.on_date_change, calen)
        self.Bind(EVT_SELECTED, self.on_select, uut)

        sizer.Add(btn, 0)
        sizer.Add(calen, 0)
        sizer.Add(uut, 1, wx.EXPAND)
        self.SetSizer(sizer)
        
        self.Show()

    def on_select(self, evt):
        d, s, e = evt.mydata
        print("%s\nstart:%02d:%02d\nend:%02d:%02d"%(d, s[0], s[1], e[0], e[1]))
        self.uut.add(d, s[0]+s[1]/60.0, e[0]+e[1]/60.0, "love", color='yellow', sync = 1)

    def on_date_change(self, evt):
        self.day = evt.PyGetDate()
        self.uut.change_mode_ref(self.mode, self.day)

    def on_change_button(self, evt):
        if self.mode == MODE_DAY:
            self.mode = MODE_WEEK
        elif self.mode == MODE_WEEK:
            self.mode = MODE_MONTH
        else:
            self.mode = MODE_DAY
        self.uut.change_mode_ref(self.mode, self.day)

if __name__ == '__main__':
    from SimpleCalendarItem import EVT_SELECTED
    import wx.calendar as cal
    app = wx.App()
    f = TestFrame()
    app.MainLoop()
